/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/

#include <stdio.h>

EXTERN_ENV;

include(radiosity.h)

static void add_radiosity_to_vertex(Edge *edge, long reverse, Element *elem, Vertex *p_c, long process_id);
static void _display_shaded_triangle(ElemVertex *ev1, ElemVertex *ev2, ElemVertex *ev3, Edge *e12, Edge *e23, Edge *e31, long process_id);
static void _disp_interactions(Element *elem, Interaction *inter, long mode, long process_id);
static void _ps_disp_interactions(Element *elem, Interaction *inter, long mode, long process_id);

/************************************************************************
 *
 *    radiosity_averaging
 *
 *************************************************************************/

void radiosity_averaging(Element *elem, long mode, long process_id)
{
    float inv_weight ;
    Vertex pc ;
    long reverse ;

    if( ! LEAF_ELEMENT(elem) )
        {
            create_radavg_task( elem->center, mode, process_id ) ;
            create_radavg_task( elem->top,    mode, process_id ) ;
            create_radavg_task( elem->right,  mode, process_id ) ;
            create_radavg_task( elem->left,   mode, process_id ) ;
            return ;
        }

    else if( mode == RAD_AVERAGING_MODE )
        {
            /* Compute center point */
            center_point( &elem->ev1->p, &elem->ev2->p, &elem->ev3->p, &pc ) ;

            reverse = EDGE_REVERSE( elem->e12, elem->ev1, elem->ev2 ) ;
            foreach_leaf_edge( elem->e12, reverse, (void (*)())add_radiosity_to_vertex, (long)elem, (long)&pc, process_id ) ;
            reverse = EDGE_REVERSE( elem->e23, elem->ev2, elem->ev3 ) ;
            foreach_leaf_edge( elem->e23, reverse, (void (*)())add_radiosity_to_vertex, (long)elem, (long)&pc, process_id ) ;
            reverse = EDGE_REVERSE( elem->e31, elem->ev3, elem->ev1 ) ;
            foreach_leaf_edge( elem->e31, reverse, (void (*)())add_radiosity_to_vertex, (long)elem, (long)&pc, process_id ) ;
        }
    else
        {
            /* Normalize it */
            LOCK(elem->ev1->ev_lock->lock);
            if( elem->ev1->weight != 1.0 )
                {
                    inv_weight = (float)1.0 / elem->ev1->weight  ;
                    elem->ev1->col.r *= inv_weight ;
                    elem->ev1->col.g *= inv_weight ;
                    elem->ev1->col.b *= inv_weight ;
                    elem->ev1->weight = 1.0 ;
                }
            UNLOCK(elem->ev1->ev_lock->lock);

            LOCK(elem->ev2->ev_lock->lock);
            if( elem->ev2->weight != 1.0 )
                {
                    inv_weight = (float)1.0 / elem->ev2->weight  ;
                    elem->ev2->col.r *= inv_weight ;
                    elem->ev2->col.g *= inv_weight ;
                    elem->ev2->col.b *= inv_weight ;
                    elem->ev2->weight = 1.0 ;
                }
            UNLOCK(elem->ev2->ev_lock->lock);

            LOCK(elem->ev3->ev_lock->lock);
            if( elem->ev3->weight != 1.0 )
                {
                    inv_weight = (float)1.0 / elem->ev3->weight  ;
                    elem->ev3->col.r *= inv_weight ;
                    elem->ev3->col.g *= inv_weight ;
                    elem->ev3->col.b *= inv_weight ;
                    elem->ev3->weight = 1.0 ;
                }
            UNLOCK(elem->ev3->ev_lock->lock);
        }
}

static void add_radiosity_to_vertex(Edge *edge, long reverse, Element *elem, Vertex *p_c, long process_id)
{
    ElemVertex *ev ;
    float weight ;

    if( reverse )
        ev = edge->pb ;
    else
        ev = edge->pa ;

    weight = (float)1.0 / distance( &ev->p, p_c ) ;
    weight = 1.0 ;
    weight = elem->area ;
    LOCK(ev->ev_lock->lock);
    ev->col.r += (elem->rad.r * weight) ;
    ev->col.g += (elem->rad.g * weight) ;
    ev->col.b += (elem->rad.b * weight) ;
    ev->weight += weight ;
    UNLOCK(ev->ev_lock->lock);
}


/************************************************************************
 *
 *    setup_view()
 *
 *************************************************************************/

Vertex view_vec ;  /* Origin to the viewer */
static float view_rot_x, view_rot_y, view_dist, view_zoom ;


void setup_view(float rot_x, float rot_y, float dist, float zoom, long process_id)
{
    Vertex v1, v2 ;
    float cc, ss ;

    /* Save parameters */
    view_rot_x = rot_x ;
    view_rot_y = rot_y ;
    view_dist  = dist ;
    view_zoom  = zoom ;

    /* Compute view vector */
    v1.x = 0.0 ;
    v1.y = 0.0 ;
    v1.z = 1.0 ;

    /* Rotate view vector */
    cc = cos( -rot_x * (M_PI / 180.0) ) ;
    ss = sin( -rot_x * (M_PI / 180.0) ) ;
    v2.x = v1.x ;
    v2.y = cc * v1.y - ss * v1.z ;
    v2.z = ss * v1.y + cc * v1.z ;

    cc = cos( -rot_y * (M_PI / 180.0) ) ;
    ss = sin( -rot_y * (M_PI / 180.0) ) ;
    v1.z = cc * v2.z - ss * v2.x ;
    v1.x = ss * v2.z + cc * v2.x ;
    v1.y = v2.y ;

    /* Store view vector */
    view_vec = v1 ;
}


/************************************************************************
 *
 *    display_scene()
 *
 *************************************************************************/

void display_scene(long fill_sw, long patch_sw, long mesh_sw, long interaction_sw, long process_id)
{
    /* Clear the screen */
    g_clear() ;

    /* Set matrix */
    g_setup_view( view_rot_x, view_rot_y, view_dist, view_zoom ) ;

    if( fill_sw == 2 )
        {
            /* Fill surfaces */
            display_elements_in_bsp_tree( DISPLAY_SHADED, process_id ) ;
        }
    if( fill_sw == 1 )
        {
            /* Fill surfaces */
            display_elements_in_bsp_tree( DISPLAY_FILLED, process_id ) ;
        }
    if( mesh_sw )
        {
            /* Draw mesh */
            g_color( G_BLUE ) ;
            display_elements_in_bsp_tree( DISPLAY_EDGEONLY, process_id ) ;
        }
    if( patch_sw )
        {
            g_color( G_RED ) ;
            display_patches_in_bsp_tree( DISPLAY_EDGEONLY, process_id ) ;
        }
    if( interaction_sw )
        {
            g_color( G_GREEN ) ;
            display_interactions_in_bsp_tree(process_id) ;
        }

    /* Flush */
    g_flush() ;
}

/************************************************************************
 *
 *    display_patch()
 *
 *************************************************************************/

void display_patch(Patch *patch, long mode, long process_id)
{
    Vertex p_buf[4] ;
    Rgb   c_buf[4] ;

    if( mode == DISPLAY_SHADED )
        {
            if( inner_product( &patch->plane_equ.n, &view_vec ) < F_ZERO )
                return ;

            p_buf[0] = patch->p1 ;
            p_buf[1] = patch->p2 ;
            p_buf[2] = patch->p3 ;
            c_buf[0] = patch->color ;
            c_buf[1] = patch->color ;
            c_buf[2] = patch->color ;

            g_spolygon( 3, p_buf, c_buf ) ;
        }
    else if( mode == DISPLAY_FILLED )
        {
            if( inner_product( &patch->plane_equ.n, &view_vec ) < F_ZERO )
                return ;

            p_buf[0] = patch->p1 ;
            p_buf[1] = patch->p2 ;
            p_buf[2] = patch->p3 ;

            g_polygon( 3, p_buf ) ;
        }
    else
        {
            g_line( &patch->p1, &patch->p2 ) ;
            g_line( &patch->p2, &patch->p3 ) ;
            g_line( &patch->p3, &patch->p1 ) ;
        }
}


/************************************************************************
 *
 *    display_patches_in_bsp_tree()
 *
 *************************************************************************/

void display_patches_in_bsp_tree(long mode, long process_id)
{
    foreach_depth_sorted_patch( &view_vec, (void (*)())display_patch, (long)mode, process_id ) ;
}



/************************************************************************
 *
 *    display_element()
 *
 *************************************************************************/

void display_element(Element *element, long mode, long process_id)
{
    Vertex p_buf[4] ;

    if( inner_product( &element->patch->plane_equ.n, &view_vec ) < F_ZERO )
        return ;

    if( mode == DISPLAY_SHADED )
        {
            _display_shaded_triangle( element->ev1, element->ev2,
                                     element->ev3,
                                     element->e12, element->e23, element->e31, process_id ) ;
        }
    else if( mode == DISPLAY_FILLED )
        {
            g_rgb( element->rad ) ;
            p_buf[0] = element->ev1->p ;
            p_buf[1] = element->ev2->p ;
            p_buf[2] = element->ev3->p ;

            g_polygon( 3, p_buf ) ;
        }
    else
        {
            g_line( &element->ev1->p, &element->ev2->p ) ;
            g_line( &element->ev2->p, &element->ev3->p ) ;
            g_line( &element->ev3->p, &element->ev1->p ) ;
        }
}

static void _display_shaded_triangle(ElemVertex *ev1, ElemVertex *ev2, ElemVertex *ev3, Edge *e12, Edge *e23, Edge *e31, long process_id)
{
    Vertex p_buf[4] ;
    Rgb   c_buf[4] ;

    p_buf[0] = ev1->p ;
    p_buf[1] = ev2->p ;
    p_buf[2] = ev3->p ;
    c_buf[0] = ev1->col ;
    c_buf[1] = ev2->col ;
    c_buf[2] = ev3->col ;
    g_spolygon( 3, p_buf, c_buf ) ;
}


/************************************************************************
 *
 *    display_elements_in_patch()
 *
 *************************************************************************/

void display_elements_in_patch(Patch *patch, long mode, long process_id)
{
    foreach_leaf_element_in_patch( patch, (void (*)())display_element, mode, process_id ) ;
    g_flush() ;
}


/************************************************************************
 *
 *    display_elements_in_bsp_tree()
 *
 *************************************************************************/

void display_elements_in_bsp_tree(long mode, long process_id)
{
    foreach_depth_sorted_patch( &view_vec, (void (*)())display_elements_in_patch, mode, process_id );
}

/************************************************************************
 *
 *    display_interactions_in_element()
 *
 *************************************************************************/

void display_interactions_in_element(Element *elem, long mode, long process_id)
{

    foreach_interaction_in_element( elem, (void (*)())_disp_interactions, mode, process_id ) ;
    g_flush() ;
}


static void _disp_interactions(Element *elem, Interaction *inter, long mode, long process_id)
{
    Vertex pa, pb ;
    Element *edst ;


    /* Display interactions only with a particular patch */
    if(   (mode == DISPLAY_HALF_INTERACTIONS)
       && (inter->destination->patch->seq_no >= elem->patch->seq_no ) )
        return ;

    /* Compute mid point of the element */
    edst = inter->destination ;
    center_point( &elem->ev1->p, &elem->ev2->p, &elem->ev3->p, &pa ) ;
    center_point( &edst->ev1->p, &edst->ev2->p, &edst->ev3->p, &pb ) ;

    /* Draw a line */
    g_line( &pa, &pb ) ;
}



/************************************************************************
 *
 *    display_interactions_in_patch
 *
 *************************************************************************/

void display_interactions_in_patch(Patch *patch, long mode, long process_id)
{
    foreach_element_in_patch( patch, (void (*)())display_interactions_in_element, mode, process_id );
}

/************************************************************************
 *
 *    display_interactions_in_bsp_tree
 *
 *************************************************************************/

void display_interactions_in_bsp_tree(long process_id)
{
    foreach_patch_in_bsp( (void (*)())display_interactions_in_patch, DISPLAY_ALL_INTERACTIONS, process_id ) ;
}



/************************************************************************
 *************************************************************************
 *
 *  PostScript Version driver
 *
 *************************************************************************
 *************************************************************************/

/************************************************************************
 *
 *    ps_display_scene()
 *
 *************************************************************************/


void ps_display_scene(long fill_sw, long patch_sw, long mesh_sw, long interaction_sw, long process_id)
{
    if( fill_sw )
        {
            /* Fill surfaces */
            ps_display_elements_in_bsp_tree( DISPLAY_SHADED, process_id ) ;
        }
    if( mesh_sw )
        {
            /* Draw mesh */
            ps_linewidth( 0.5 ) ;
            ps_display_elements_in_bsp_tree( DISPLAY_EDGEONLY, process_id ) ;
        }
    if( patch_sw )
        {
            /* Draw patches */
            ps_linewidth( 1.2 ) ;
            ps_display_patches_in_bsp_tree( DISPLAY_EDGEONLY, process_id ) ;
        }
    if( interaction_sw )
        {
            /* Draw interactions */
            ps_linewidth( 0.2 ) ;
            ps_display_interactions_in_bsp_tree(process_id) ;
        }

}

/************************************************************************
 *
 *    ps_display_patch()
 *
 *************************************************************************/

void ps_display_patch(Patch *patch, long mode, long process_id)
{
    Vertex p_buf[4] ;
    Rgb   c_buf[4] ;

    if( mode == DISPLAY_SHADED )
        {
            if( inner_product( &patch->plane_equ.n, &view_vec ) < F_ZERO )
                return ;
            p_buf[0] = patch->p1 ;
            p_buf[1] = patch->p2 ;
            p_buf[2] = patch->p3 ;
            c_buf[0] = patch->color ;
            c_buf[1] = patch->color ;
            c_buf[2] = patch->color ;

            ps_spolygon( 3, p_buf, c_buf ) ;
        }
    else if( mode == DISPLAY_FILLED )
        {
            if( inner_product( &patch->plane_equ.n, &view_vec ) < F_ZERO )
                return ;
            p_buf[0] = patch->p1 ;
            p_buf[1] = patch->p2 ;
            p_buf[2] = patch->p3 ;

            ps_polygon( 3, p_buf ) ;
        }
    else
        {
            p_buf[0] = patch->p1 ;
            p_buf[1] = patch->p2 ;
            p_buf[2] = patch->p3 ;

            ps_polygonedge( 3, p_buf ) ;
        }
}


/************************************************************************
 *
 *    ps_display_patches_in_bsp_tree()
 *
 *************************************************************************/

void ps_display_patches_in_bsp_tree(long mode, long process_id)
{
    foreach_depth_sorted_patch( &view_vec, (void (*)())ps_display_patch, (long)mode, process_id ) ;
}



/************************************************************************
 *
 *    ps_display_element()
 *
 *************************************************************************/

void ps_display_element(Element *element, long mode, long process_id)
{
    Vertex p_buf[4] ;
    Rgb   c_buf[4] ;

    if( mode == DISPLAY_SHADED )
        {
            if( inner_product( &element->patch->plane_equ.n, &view_vec )
               < F_ZERO )
                return ;
            p_buf[0] = element->ev1->p ;
            p_buf[1] = element->ev2->p ;
            p_buf[2] = element->ev3->p ;
            c_buf[0] = element->rad ;
            c_buf[1] = element->rad ;
            c_buf[2] = element->rad ;

            ps_spolygon( 3, p_buf, c_buf ) ;
        }
    else if( mode == DISPLAY_FILLED )
        {
            if( inner_product( &element->patch->plane_equ.n, &view_vec )
               < F_ZERO )
                return ;
            p_buf[0] = element->ev1->p ;
            p_buf[1] = element->ev2->p ;
            p_buf[2] = element->ev3->p ;

            ps_polygon( 3, p_buf ) ;
        }
    else
        {
            p_buf[0] = element->ev1->p ;
            p_buf[1] = element->ev2->p ;
            p_buf[2] = element->ev3->p ;

            ps_polygonedge( 3, p_buf ) ;
        }
}


/************************************************************************
 *
 *    ps_display_elements_in_patch()
 *
 *************************************************************************/

void ps_display_elements_in_patch(Patch *patch, long mode, long process_id)
{
    foreach_leaf_element_in_patch( patch, (void (*)())ps_display_element, mode, process_id ) ;
}


/************************************************************************
 *
 *    ps_display_elements_in_bsp_tree()
 *
 *************************************************************************/

void ps_display_elements_in_bsp_tree(long mode, long process_id)
{
    foreach_depth_sorted_patch( &view_vec, (void (*)())ps_display_elements_in_patch, mode, process_id ) ;
}

/************************************************************************
 *
 *    ps_display_interactions_in_element()
 *
 *************************************************************************/

void ps_display_interactions_in_element(Element *elem, long mode, long process_id)
{
    foreach_interaction_in_element( elem, (void (*)())_ps_disp_interactions, mode, process_id ) ;
}


static void _ps_disp_interactions(Element *elem, Interaction *inter, long mode, long process_id)
{
    Vertex pa, pb ;
    Element *edst ;

    /* Display interactions only with a particular patch */
    if(   (mode == DISPLAY_HALF_INTERACTIONS)
       && (inter->destination->patch->seq_no >= elem->patch->seq_no ) )
        return ;

    /* Compute mid point of the element */
    edst = inter->destination ;
    center_point( &elem->ev1->p, &elem->ev2->p, &elem->ev3->p, &pa ) ;
    center_point( &edst->ev1->p, &edst->ev2->p, &edst->ev3->p, &pb ) ;

    /* Draw a line */
    ps_line( &pa, &pb ) ;
}



/************************************************************************
 *
 *    ps_display_interactions_in_patch
 *
 *************************************************************************/

void ps_display_interactions_in_patch(Patch *patch, long mode, long process_id)
{
    foreach_element_in_patch( patch, (void (*)())ps_display_interactions_in_element, mode, process_id );
}

/************************************************************************
 *
 *    ps_display_interactions_in_bsp_tree
 *
 *************************************************************************/

void ps_display_interactions_in_bsp_tree(long process_id)
{
    foreach_patch_in_bsp( (void (*)())ps_display_interactions_in_patch, DISPLAY_ALL_INTERACTIONS, process_id ) ;
}

